<template>
    <div class="container">
        <div v-if="propJson.uploadType === 'button'" style="margin-bottom: 3%">
            <label :class="propJson.editFlag ? 'editFileComponentButtonClass' : 'fileComponentButtonClass'">
                    <span class="fileComponentSpanClass" :style="execButtonSpanStyle">
                        <component is="UploadOutlined" style="margin-right: 1%"/>
                        上传
                    </span>
                <input type="file" class="fileInput" @change="changeFn">
            </label>
        </div>

        <div v-if="propJson.uploadType === 'icon'" style="margin-bottom: 3%">
            <label :class="propJson.editFlag ? 'editFileComponentIconClass' : 'fileComponentIconClass'" :style="execIconLabel">
                <span :style="execIconSpan" style="display: flex; align-items: center; justify-content: center">
                    <div style="">
                        <component is="PlusOutlined" :style="execIconComponent"/>
                        <span :style="execIconTipSpan">
                            点击上传文件
                        </span>
                    </div>
                </span>
                <input type="file" class="fileInput" @change="changeFn">
            </label>
        </div>

        <div v-if="percentageFlag" style="width: 100%; margin-bottom: 3%">
            <el-progress :percentage="percentageNum" :status="percentageStatus"/>
        </div>
    </div>
</template>

<script name="FileUploadMultiComponent" setup lang="ts">
import {deepCopy, setJsonValue} from "@/utils/ywuCommon";
import {doUploadApi, getBuildParam} from "@/utils/lowcode/thirdService";
import {ElMessage, ElNotification} from "element-plus";
import {computed, defineProps, ref} from "vue";

let props = defineProps(['propJson', 'commonParam']);

let propJson = computed(() => {return props['propJson']})
let commonParam = computed(() => {return props['commonParam']})

let percentageFlag = ref(false);
let percentageNum = ref(0);
let percentageStatus = ref("");

let realPartSize = computed(() => {
    let partSize = parseInt(propJson.value.partSize);
    let resPartSize = partSize
    let partUnit = propJson.value.partSizeUnit
    if (partUnit === 'b') {
        resPartSize = partSize;
    } else if (partUnit === 'kb') {
        resPartSize = 1024 * partSize;
    } else if (partUnit === 'mb') {
        resPartSize = 1024 * 1024 * partSize;
    }
    return resPartSize;
})

let execButtonSpanStyle = computed(() => {
    let style = "";
    if (propJson.value.uploadType !== 'button') {
        return style;
    }
    if (propJson.value.buttonFontSize) {
        style += "font-size: " + propJson.value.buttonFontSize + "px;";
        style += "line-height: " + 2 * parseInt(propJson.value.buttonFontSize) + "px;";
        style += "border-radius: " + parseInt(propJson.value.buttonFontSize) / 3 + "px;";
    }
    return style
})

let execIconLabel = computed(() => {
    let style = "";
    if (propJson.value.iconFontHeight) {
        style += "height: " + propJson.value.iconFontHeight + "px;";
        style += "border-radius: " + propJson.value.iconFontHeight / 30 + "px;";
    }
    return style
})

let execIconSpan = computed(() => {
    let style = "display: block;";
    if (propJson.value.iconFontHeight) {
        style += "height: " + propJson.value.iconFontHeight + "px;";
    }
    return style
})

let execIconTipSpan = computed(() => {
    let style = "display: block;text-align: center;width: 100%;color: #a8abb2;";
    if (propJson.value.iconFontHeight) {
        style += "font-size: " + propJson.value.iconFontHeight / 15 + "px;";
    }
    return style
})

let execIconComponent = computed(() => {
    let style = "text-align: center;width: 100%;color: #a8abb2;";
    if (propJson.value.iconFontHeight) {
        style += "font-size: " + propJson.value.iconFontSize + "px;";
    }
    return style
})

function splitFile(partNum, file) {
    const partFiles = [];
    let sliceFileIndex = 0;
    let innerRealPartSize = realPartSize.value
    // 获取原始文件的扩展名
    const fileExtension = file.name.split('.').pop();
    for (let index = 0; index < partNum; index++) {
        const blob = file.slice(sliceFileIndex, sliceFileIndex + innerRealPartSize);
        // 创建带有正确扩展名的新文件对象
        // 文件名格式: 原始文件名_片段索引.扩展名
        const fileName = `${file.name.replace(`.${fileExtension}`, '')}_${index}.${fileExtension}`;
        const fileWithExt = new File([blob], fileName, { type: file.type });
        partFiles.push(fileWithExt);
        sliceFileIndex += innerRealPartSize;
    }
    return partFiles;
}

function execCatch(error) {
    ElMessage({
        message: '上传失败',
        type: 'error',
    })
    percentageStatus.value = 'exception'
}

function finishThen() {
    ElMessage({
        message: '上传完成',
        type: 'success',
    })
    percentageStatus.value = 'success'
}

async function changeFn(e) {
    // 进度条重置为0
    percentageFlag.value = true
    percentageNum.value = 0;
    // 获取待上传的文件
    const file = e.target.files[0];
    const fileName = file.name;
    // 分段数
    let realPartSizeInfo = realPartSize.value
    const partNum = Math.ceil(file.size / realPartSizeInfo);
    // 切割分段文件
    const partFiles = splitFile(partNum, file);
    // 记录全局请求变量信息
    let globalRequestParam = []
    // 执行上传前置请求
    await doUploadBeforeAction(fileName, partNum, globalRequestParam);
    // 弹出提示框
    ElNotification({
        title: '提示',
        message: "文件上传中，请勿刷新页面！！",
        type: 'warning'
    })
    // 执行上传文件方法
    await doUploadAction(fileName, partNum, globalRequestParam, partFiles);
    // 执行上传文件校验方法
    await doUploadCheckAction(fileName, partNum, globalRequestParam, partFiles);
    // 执行上传文件合并方法
    await doUploadMergeAction(fileName, partNum, globalRequestParam);
}

async function doUploadBeforeAction(fileName, partNum, globalRequestParam) {
    // 根据配置的内置参数映射为全局变量中的字段
    let beforeActionRequestVariable = propJson.value.doUploadBeforeActionInnerRequestVariable;
    // 多个内置变量挨个处理  构建参数
    let beforeAction = deepCopy(propJson.value.doUploadBeforeAction)
    let partNumRequestVariable = beforeActionRequestVariable[0]
    if (partNumRequestVariable.variableName) {
        let tempVariableInfo = {variableName: beforeActionRequestVariable[0].variableName, variableValue: partNum, type: "custom", variableType: "String", variableParamName: ""}
        beforeAction.apiParamList.push(tempVariableInfo)
    }
    let fileNameRequestVariable = beforeActionRequestVariable[1]
    if (fileNameRequestVariable.variableName) {
        let tempVariableInfo = {variableName: beforeActionRequestVariable[1].variableName, variableValue: fileName, type: "custom", variableType: "String", variableParamName: ""}
        beforeAction.apiParamList.push(tempVariableInfo)
    }
    let res = await doUploadApi(beforeAction, commonParam.value, () => {}, execCatch, {}, {}, false)
    for (let objName in res) {
        let prop = res[objName]
        if (objName.startsWith("$.")) {
            setJsonValue(globalRequestParam, objName, prop)
        } else {
            globalRequestParam[objName] = prop
        }
    }
}

async function doUploadAction(fileName, partNum, globalRequestParam, partFiles) {
    // 此变量用作计算进度百分比
    let currentDoIndex = 1;
    // 遍历所有分段文件，挨个上传
    for (let index = 0; index < partNum; index++) {
        let formData = getFormDataInfo(index, partFiles, partNum, fileName, globalRequestParam);
        let res = await doUploadApi(propJson.value.doUploadAction, commonParam.value, () => {}, execCatch, {}, formData, true)
        // 接收返参
        for (let objName in res) {
            let prop = res[objName]
            if (objName.startsWith("$.")) {
                setJsonValue(globalRequestParam, objName, prop)
            } else {
                globalRequestParam[objName] = prop
            }
        }
        // 计算进度
        percentageNum.value = (currentDoIndex / partNum * 100).toFixed(2);
        currentDoIndex += 1;
    }
}

async function doUploadCheckAction(fileName, partNum, globalRequestParam, partFiles) {
    // 未配置参数，直接放行
    if (!propJson.value.doUploadCheckAction.apiPath) {
        return;
    }
    let level = 0
    while (level < propJson.value.maxCheckLevel) {
        // 根据配置的内置参数映射为全局变量中的字段
        let uploadCheckActionRequestVariable = propJson.value.doUploadCheckActionInnerRequestVariable;
        // 多个内置变量挨个处理  构建参数
        let checkAction = deepCopy(propJson.value.doUploadCheckAction)
        let partNumRequestVariable = uploadCheckActionRequestVariable[0]
        if (partNumRequestVariable.variableName) {
            let tempVariableInfo = {variableName: uploadCheckActionRequestVariable[0].variableName, variableValue: partNum, type: "custom", variableType: "String", variableParamName: ""}
            checkAction.apiParamList.push(tempVariableInfo)
        }
        let fileNameRequestVariable = uploadCheckActionRequestVariable[1]
        if (fileNameRequestVariable.variableName) {
            let tempVariableInfo = {variableName: uploadCheckActionRequestVariable[1].variableName, variableValue: fileName, type: "custom", variableType: "String", variableParamName: ""}
            checkAction.apiParamList.push(tempVariableInfo)
        }
        // 构建全局参数
        let realParam = getBuildParam(checkAction, commonParam.value);
        for (let objName in globalRequestParam) {
            realParam[objName] = globalRequestParam[objName]
        }
        let res = await doUploadApi(checkAction, commonParam.value, () => {}, execCatch, {}, realParam, true)
        for (let objName in res) {
            let prop = res[objName]
            if (objName.startsWith("$.")) {
                setJsonValue(globalRequestParam, objName, prop)
            } else {
                globalRequestParam[objName] = prop
            }
        }
        // 返回值必须配置 $.resIds 字段，用于重新上传分段文件内容
        let resIds = globalRequestParam.resIds
        if (resIds && resIds.length > 0) {
            // 根据返回的异常分段数重新进行上传
            for (let resIdIndex = 0; resIdIndex < resIds.length; resIdIndex++) {
                let execIndex = parseInt(resIds[resIdIndex])
                let formData = getFormDataInfo(execIndex, partFiles, partNum, fileName, globalRequestParam);
                let res = await doUploadApi(propJson.value.doUploadAction, commonParam.value, () => {}, execCatch, {}, formData, true)
                // 接收返参
                for (let objName in res) {
                    let prop = res[objName]
                    if (objName.startsWith("$.")) {
                        setJsonValue(globalRequestParam, objName, prop)
                    } else {
                        globalRequestParam[objName] = prop
                    }
                }
            }
        } else {
            break;
        }
        level += 1;
    }
}

async function doUploadMergeAction(fileName, partNum, globalRequestParam) {
    // 未配置参数，直接放行
    if (!propJson.value.doUploadMergeAction.apiPath) {
        return;
    }
    // 根据配置的内置参数映射为全局变量中的字段
    let uploadMergeActionRequestVariable = propJson.value.doUploadMergeActionInnerRequestVariable;
    // 多个内置变量挨个处理  构建参数
    let mergeAction = deepCopy(propJson.value.doUploadMergeAction)
    let partNumRequestVariable = uploadMergeActionRequestVariable[0]
    if (partNumRequestVariable.variableName) {
        let tempVariableInfo = {variableName: uploadMergeActionRequestVariable[0].variableName, variableValue: partNum, type: "custom", variableType: "String", variableParamName: ""}
        mergeAction.apiParamList.push(tempVariableInfo)
    }
    let fileNameRequestVariable = uploadMergeActionRequestVariable[1]
    if (fileNameRequestVariable.variableName) {
        let tempVariableInfo = {variableName: uploadMergeActionRequestVariable[1].variableName, variableValue: fileName, type: "custom", variableType: "String", variableParamName: ""}
        mergeAction.apiParamList.push(tempVariableInfo)
    }
    // 构建全局参数
    let realParam = await getBuildParam(mergeAction, commonParam.value);
    for (let objName in globalRequestParam) {
        realParam[objName] = globalRequestParam[objName]
    }
    let res = await doUploadApi(mergeAction, commonParam.value, finishThen, execCatch, {}, realParam, true)
    for (let objName in res) {
        let prop = res[objName]
        if (objName.startsWith("$.")) {
            setJsonValue(globalRequestParam, objName, prop)
        } else {
            globalRequestParam[objName] = prop
        }
    }
}

function getFormDataInfo(index, partFiles, partNum, fileName, globalRequestParam) {
    const multiFile = partFiles[index];
    let formData = new FormData();
    // 根据配置的内置参数映射为全局变量中的字段
    let uploadActionRequestVariable = propJson.value.doUploadActionInnerRequestVariable;
    let partNumRequestVariable = uploadActionRequestVariable[0]
    if (partNumRequestVariable.variableName) {
        let variableName = partNumRequestVariable.variableName;
        if (partNumRequestVariable.variableName.startsWith("$.")) {
            variableName = variableName.substring(2, variableName.length);
        }
        formData.append(variableName.toString(), partNum)
    }
    let multiFileRequestVariable = uploadActionRequestVariable[1]
    if (multiFileRequestVariable.variableName) {
        let variableName = multiFileRequestVariable.variableName;
        if (multiFileRequestVariable.variableName.startsWith("$.")) {
            variableName = variableName.substring(2, variableName.length);
        }
        formData.append(variableName, multiFile)
    }
    let fileNameRequestVariable = uploadActionRequestVariable[2]
    if (fileNameRequestVariable.variableName) {
        let variableName = fileNameRequestVariable.variableName;
        if (fileNameRequestVariable.variableName.startsWith("$.")) {
            variableName = variableName.substring(2, variableName.length);
        }
        formData.append(variableName, fileName)
    }
    let segmentIdRequestVariable = uploadActionRequestVariable[3]
    if (segmentIdRequestVariable.variableName) {
        let variableName = segmentIdRequestVariable.variableName;
        if (segmentIdRequestVariable.variableName.startsWith("$.")) {
            variableName = variableName.substring(2, variableName.length);
        }
        formData.append(variableName, index)
    }
    // 构建全局参数
    for (let objName in globalRequestParam) {
        let prop = globalRequestParam[objName]
        formData.append(objName, prop)
    }
    return formData;
}
</script>

<style scoped lang="less">
.fileInput {
    display: none;
}

.editFileComponentButtonClass {
    width: 90%;
    display: block;
    margin-left: 5%;
    .fileComponentSpanClass {
        width: 100%;
        display: block;
        background-color: #409eff;
        text-align: center;
    }
    .fileComponentSpanClass:hover {
        cursor: pointer;
        background-color: #a0cfff;
    }
    //去除光标
    caret-color: transparent;
}

.fileComponentButtonClass {
    width: 98%;
    display: block;
    padding: 1%;
    .fileComponentSpanClass {
        width: 100%;
        display: block;
        background-color: #409eff;
        text-align: center;
    }
    .fileComponentSpanClass:hover {
        cursor: pointer;
        background-color: #a0cfff;
    }
    //去除光标
    caret-color: transparent;
}

.editFileComponentIconClass {
    width: 90%;
    display: block;
    margin-left: 5%;
    border: 1px dashed #dcdfe6;
    //去除光标
    caret-color: transparent;
}

.fileComponentIconClass {
    width: 98%;
    margin-left: 1%;
    display: block;
    border: 1px dashed #dcdfe6;
    //去除光标
    caret-color: transparent;
}

.fileComponentIconClass:hover {
    cursor: pointer;
    border: 1px dashed #409eff;
}
</style>